from datetime import datetime
from sqlalchemy.orm import Session
from app.monitor_metrics.models.prom_host_info import PromHostInfo
from app.monitor_metrics.models.prom_host_group import PromHostGroup
from app.manage_notice.models.manage_notice_team import ManageNoticeTeam
from app.monitor_metrics.models.prom_alarm_template import PromAlarmTemplate
from app.monitor_metrics.handlers.prom_host_info_handler import PromHostInfoHandler
from app.monitor_metrics.handlers.prom_alarm_template_handler import PromAlarmTemplateHandler
from common.consul_client import ConsulClient
from core.response import resp_400, resp_500, HTTPExceptionCustom
from core.logger import logger
from app.celery_task.models.celery_task import CeleryTask


class PromHostGroupHandler():
    """  host group Handler
    """
    def __init__(self):
        self.prom_host_info_handler = PromHostInfoHandler()
        self.prom_alarm_template_handler = PromAlarmTemplateHandler()


    # ==== 主机组自身信息的增删改查操作 ====
    def get_host_group_search(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段模糊匹配，获取主机组信息
        """
        try:
            field_info = {field: value}
            order_info = {"department_uuid":"asc", "name":"asc"}
            result = PromHostGroup.filter_by_field_search(db, page, per_page, field_info, order_info)
            if not result:
                logger.error(f"get host group search, result is null, {field}:{value}")
                return False, None
        except Exception as e:
            message = f"get host group search by {field}:{value} error => {e}"
            logger.exception(message)
            return resp_500(message)

        return True, result

    def get_host_group_match(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段完全匹配，获取主机组信息
        """
        try:
            result = PromHostGroup.filter_by_field_match(db, page, per_page, {field: value})
            if not result:
                logger.error(f"get host group match, result is null, {field}:{value}")
                return False, None
        except Exception as e:
            message = f"get host group match by {field}:{value} error => {e}"
            logger.exception(message)
            return resp_500(message)

        return True, result


    def get_host_group_hosts(self, db: Session, uuid: int):
        """
        根据表字段完全匹配，获取主机组信息
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, uuid)
            if not host_group_object:
                # 如果存在， 则异常返回
                logger.error(f"get host group hosts failed, uuid:{uuid} was exist")
                return False, None

            result = PromHostInfo.get_object_info_by_group_uuid(db, uuid)
            # 过滤内部标签
            for host in result:
                host["labels"] = {
                    key: value
                    for key, value in host["labels"].items()
                    if not key.startswith("i_")
                }
        except Exception as e:
            message = f"get host group hosts uuid:{uuid} error => {e}"
            logger.exception(message)
            return resp_500(message)

        return True, result

    def get_host_group_template(self, db: Session, uuid: int):
        """
        获取主机组绑定的，告警模板详细信息
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, uuid)
            if not host_group_object:
                # 如果存在， 则异常返回
                logger.error(f"get host group template failed, uuid:{uuid} was exist")
                return False, None

            result_info = []
            for template_uuid in host_group_object.templates_uuid:
                template_info =  PromAlarmTemplate.get_object_info_by_uuid(db, template_uuid)
                result_info.append(template_info)
        except Exception as e:
            message = f"get host group:{uuid} template error => {e}"
            logger.exception(message)
            return resp_500(message)

        return True, result_info


    def get_host_group_notice(self, db: Session, uuid: int):
        """
        获取主机组指定的通知小组详细信息
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, uuid)
            if not host_group_object:
                # 如果存在， 则异常返回
                logger.error(f"get host group notice team failed, uuid:{uuid} was exist")
                return False, None

            result_info = []
            for notice_team_uuid in host_group_object.notice_teams_uuid:
                notice_team_info = ManageNoticeTeam.get_object_info_by_uuid(db, notice_team_uuid)
                result_info.append(notice_team_info)
        except Exception as e:
            message = f"get host group:{uuid} notice team error => {e}"
            logger.exception(message)
            return resp_500(message)

        return True, result_info



    def add_group(self, db: Session, data: dict):
        """
        创建主机组
        """
        try:
            host_group_object = PromHostGroup.get_object_by_name(db, data["name"])
            if host_group_object:
                # 如果存在， 则异常返回
                logger.error(f"add host group failed,{data['name']} was exist")
                return False

            result = PromHostGroup.add(db, data)
            if not result:
                logger.error(f"add host group to database failed")
                return False
        except Exception as e:
            message = f"add host group error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return True



    def update_group(self, db: Session, data: dict):
        """
        修改主机组信息
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, data["uuid"])
            if not host_group_object:
                # 如果不存在， 则异常返回
                logger.error(f"update host group failed, uuid:{data['uuid']} not exist")
                return False

            # 更新主机组的信息
            result = PromHostGroup.update(db, data)
            if not result:
                logger.error(f"update host group failed")
                return False
        except Exception as e:
            message = f"update group failed error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return True



    def delete_group(self, db: Session, data: dict):
        """
        删除主机组信息
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, data['uuid'])
            if not host_group_object:
                # 如果不存在， 则异常返回
                logger.error(f"delete host group failed, uuid:{data['uuid']} not exist")
                return False

            # 主机组内还有服务器，不能删除
            if len(host_group_object.hosts) != 0:
                logger.error(f"delete host group failed, name:{host_group_object.name} has hosts in it")
                return False


            # 主机组还有绑定的告警模板，不能删除
            host_group_info = PromHostGroup.get_object_info_by_uuid(db, data['uuid'])
            if len(host_group_info["templates_uuid"]) != 0:
                logger.error(f"delete host group failed, name:{host_group_object.name} has templates in it")
                return False

            # 删除告主机组
            PromHostGroup.delete_object_by_uuid(db, data['uuid'])
        except Exception as e:
            message = f"delete group error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return True


    def put_host_group_notice(self, db: Session, data: dict):
        """
        更新主机组的负责人
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, data["uuid"])
            if not host_group_object:
                # 如果不存在， 则异常返回
                logger.error(f"update host group failed, uuid:{data['uuid']} not exist")
                return False

            # 启动异步任务
            result = self.task_update_notice_start(db, data)
            if not result:
                logger.error(f"task update_notice start failed")
                return False

            # # 更新主机绑定的模板
            # host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["uuid"])
            # for host_info in host_group_info["hosts"]:
            #     templates_uuid = host_group_info['templates_uuid']
            #     templates_uuid.append(host_info["template_uuid"])
            #     result = self.prom_alarm_template_handler.host_update_bind_template(db, host_info["uuid"], templates_uuid)
            #     if not result:
            #         logger.error(
            #             f"put_host_group_notice:{host_info['uuid']} update bind templates_uuid:{host_group_info['templates_uuid']} failed")
            #         return False
            #
            # # 更新主机组的信息
            # result = PromHostGroup.update(db, data)
            # if not result:
            #     logger.error(f"update alarm group to database failed")
            #     return False
        except Exception as e:
            message = f"update group failed error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return result




    def task_update_notice_start(self, db: Session, data: dict):
        """
        更新主机组负责人的异步任务启动函数
        """
        # 启动异步任务
        from app.monitor_metrics.task import prom_host_group_update_notice_async
        # 判断任务是否已经启动，控制只有一个任务在跑。 如果任务可以多个同时进行，则无需这一步判断。
        task_name = prom_host_group_update_notice_async.name.split(".")[-1]
        if CeleryTask.is_progress_on_name(db, task_name):
            logger.error(f"task: {task_name} was stared")
            return False

        task = prom_host_group_update_notice_async.delay(data)
        task_data = {
            "name": task_name,
            "tid": task.id,
            "state": task.state
        }

        logger.info(f"add task: {task_name} success")
        return task_data


    def task_update_notice_handle(self, db: Session, data: dict):
        """
        更新主机组负责人的处理函数
        """
        # 获取原始的数据
        host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["uuid"])
        notice_teams_uuid = host_group_info["notice_teams_uuid"]

        # 更新主机组的信息
        result = PromHostGroup.update(db, data)
        if not result:
            logger.error(f"host group update_notice to database failed")
            return False

        # 更新主机绑定的模板
        for host_info in host_group_info["hosts"]:
            templates_uuid = host_group_info['templates_uuid']
            templates_uuid.append(host_info["template_uuid"])
            result = self.prom_alarm_template_handler.host_update_bind_template(db, host_info["uuid"], templates_uuid)
            if not result:
                # 更新失败，回滚主机组的通知组信息
                data["notice_teams_uuid"] = notice_teams_uuid
                PromHostGroup.update(db, data)

                # 失败返回
                logger.error(f"host_uuid:{host_info['uuid']} update bind templates_uuid:{templates_uuid} failed")
                return False

        logger.info(f"host_group:{data['uuid']} update notice:{data['notice_teams_uuid']} success")
        return True




    # ==== 主机组的服务器添加和移除操作 ====
    def add_group_hosts(self, db: Session, data: dict):
        """
        主机组批量添加主机
        """
        try:
            for host_uuid in data["hosts_uuid"]:
                host_object = PromHostInfo.get_object_by_uuid(db, host_uuid)
                if not host_object:
                    logger.warning(f"add_group_hosts: host_uuid:{host_uuid} not exist")
                    continue

                # 修改主机所属的组
                host_info = PromHostInfo.get_object_info_by_uuid(db, host_uuid)

                # 绑定新的继承模板
                new_host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["host_group_uuid"])
                result = self.prom_alarm_template_handler.host_bind_template(db, host_uuid, new_host_group_info["templates_uuid"])
                if not result:
                    # 错误，回滚数据
                    self.prom_alarm_template_handler.host_unbound_template(db, host_uuid, new_host_group_info["templates_uuid"])
                    logger.error(
                        f"add_group_hosts:{host_uuid} bind new_templates_uuid:{new_host_group_info['templates_uuid']} failed")
                    return False

                # 解绑原来的继承模板
                old_host_group_info = PromHostGroup.get_object_info_by_name(db, host_info["host_group"])
                result = self.prom_alarm_template_handler.host_unbound_template(db, host_uuid, old_host_group_info["templates_uuid"])
                if not result:
                    # 回滚数据
                    self.prom_alarm_template_handler.host_unbound_template(db, host_uuid, new_host_group_info["templates_uuid"])
                    logger.error(
                        f"add_group_hosts:{host_uuid} unbound old_templates_uuid:{old_host_group_info['templates_uuid']} failed")
                    return False


                # 更新主机信息
                host_info["host_group_uuid"] = data["host_group_uuid"]
                host_info.pop("host_group")
                host_info.pop("template_name")
                result = self.prom_host_info_handler.update_host(db, host_info)
                if not result:
                    logger.error(f"add {host_info['name']}:{host_info['ip']} => {host_info['host_group_uuid']} failed")
                    return False
        except Exception as e:
            message = f"add hosts to group:{data['host_group_uuid']} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True




    def delete_group_hosts(self, db: Session, data: dict):
        """
        主机组批量移除主机
        """
        try:
            for host_uuid in data["hosts_uuid"]:
                # 判断host_uuid是否存在
                host_object = PromHostInfo.get_object_by_uuid(db, host_uuid)
                if not host_object:
                    logger.error(f"delete_group_hosts: failed, host_uuid:{host_uuid} not exist")
                    continue

                host_info = PromHostInfo.get_object_info_by_uuid(db, host_uuid)
                old_host_group_info = PromHostGroup.get_object_info_by_uuid(db, host_info["host_group_uuid"])
                # 默认组的名字和部门的名字一样
                default_group_name = old_host_group_info["department"]
                default_group_object = PromHostGroup.get_object_by_name(db, default_group_name)
                if not default_group_object:
                    logger.error(f"delete_group_hosts: failed, default_group_name:{default_group_name} not exist")
                    continue

                # 绑定默认组的继承模板
                default_group_info = PromHostGroup.get_object_info_by_uuid(db, old_host_group_info["department_uuid"])
                result = self.prom_alarm_template_handler.host_bind_template(db, host_uuid, default_group_info["templates_uuid"])
                if not result:
                    # 回滚数据
                    self.prom_alarm_template_handler.host_unbound_template(db, host_uuid, default_group_info["templates_uuid"])
                    logger.error(f"delete_group_hosts: {host_uuid} bind default_template_uuid:{default_group_info['templates_uuid']} failed")
                    return False

                # 解绑原来的继承模板
                result = self.prom_alarm_template_handler.host_unbound_template(db, host_uuid, old_host_group_info['templates_uuid'])
                if not result:
                    logger.error(f"delete_group_hosts:{host_uuid} unbound old_templates_uuid:{old_host_group_info['templates_uuid']} failed")
                    return False

                # 更新主机信息
                host_info["host_group_uuid"] = default_group_info['uuid']
                host_info.pop("host_group")
                host_info.pop("template_name")
                result = self.prom_host_info_handler.update_host(db, host_info)
                if not result:
                    logger.error(f"delete_group_hosts: update host failed data => {host_info}")
                    return False
        except Exception as e:
            message = f"delete {data['name']} group hosts error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True


    # ==== 主机组的告警模板绑定和解绑操作 ====
    def bind_template(self, db: Session, data: dict):
        """
        主机组绑定模板
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, data["host_group_uuid"])
            if not host_group_object:
                # 如果不存在， 则异常返回
                logger.error(f"bind template failed, host_group_uuid:{data['host_group_uuid']} not exist")
                return False

            alarm_template_object = PromAlarmTemplate.get_object_by_uuid(db, data["template_uuid"])
            if not alarm_template_object:
                # 如果不存在， 则异常返回
                logger.error(f"bind template failed, template_uuid:{data['template_uuid']} not exist")
                return False

            # 启动异步任务来批量处理
            result = self.task_bind_template_start(db, data)
            if not result:
                logger.error(f"task: bind template start failed")
                return False
            # # 主机组绑定模板
            # host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["host_group_uuid"])
            # for host_info in host_group_info["hosts"]:
            #     result = self.prom_alarm_template_handler.host_bind_template(db, host_info["uuid"], [data["template_uuid"]])
            #     if not result:
            #         logger.error(f"host_info_uuid:{host_info['uuid']} bind template_uuid:{data['template_uuid']} failed")
            #         return False
            #
            # # 更新数据库中主机组的信息
            # PromHostGroup.add_template(db, data["host_group_uuid"], data["template_uuid"])
        except Exception as e:
            message = f"host_group bind template error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return result


    def task_bind_template_start(self, db: Session, data: dict):
        """
        主机组绑定模板的异步任务启动函数
        """
        # 启动异步任务
        from app.monitor_metrics.task import prom_host_group_bind_template_async
        # 判断任务是否已经启动，控制只有一个任务在跑。 如果任务可以多个同时进行，则无需这一步判断。
        task_name = prom_host_group_bind_template_async.name.split(".")[-1]
        if CeleryTask.is_progress_on_name(db, task_name):
            logger.error(f"task: {task_name} was stared")
            return False

        task = prom_host_group_bind_template_async.delay(data)
        task_data = {
            "name": task_name,
            "tid": task.id,
            "state": task.state
        }

        logger.info(f"add task: {task_name} success")
        return task_data


    def task_bind_template_handle(self, db: Session, data: dict):
        """
        主机组绑定模板的处理函数
        """
        host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["host_group_uuid"])
        for host_info in host_group_info["hosts"]:
            result = self.prom_alarm_template_handler.host_bind_template(db, host_info["uuid"], [data["template_uuid"]])
            if not result:
                # 主机绑定模板失败，整个事物回滚
                for host_roll_back in host_group_info["hosts"]:
                    self.prom_alarm_template_handler.host_unbound_template(db, host_roll_back["uuid"], [data["template_uuid"]])

                # 失败退出
                logger.error(f"host_info_uuid:{host_info['uuid']} bind template_uuid:{data['template_uuid']} failed")
                return False

        # 更新数据库中主机组的信息
        PromHostGroup.add_template(db, data["host_group_uuid"], data["template_uuid"])
        logger.info(f"host_group_uuid:{data['host_group_uuid']} bind template_uuid:{data['template_uuid']} success")
        return True



    def unbound_template(self, db: Session, data: dict):
        """
        主机组解绑模板
        """
        try:
            host_group_object = PromHostGroup.get_object_by_uuid(db, data["host_group_uuid"])
            if not host_group_object:
                # 如果不存在， 则异常返回
                logger.error(f"bind template failed, host_group_uuid:{data['host_group_uuid']} not exist")
                return False

            alarm_template_object = PromAlarmTemplate.get_object_by_uuid(db, data["template_uuid"])
            if not alarm_template_object:
                # 如果不存在， 则异常返回
                logger.error(f"bind template failed, template_uuid:{data['template_uuid']} not exist")
                return False

            # 启动异步任务
            result = self.task_unbound_template_start(db, data)
            if not result:
                logger.error(f"task unbound template start failed")
                return False
            # # 主机组解绑模板
            # host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["host_group_uuid"])
            # for host_info in host_group_info["hosts"]:
            #     result = self.prom_alarm_template_handler.host_unbound_template(db, host_info["uuid"], [data["template_uuid"]])
            #     if not result:
            #         logger.error(f"host_uuid:{host_info['uuid']} unbound template_uuid:{data['template_uuid']} failed")
            #         return False
            #
            # # 更新数据库中主机组的信息
            # PromHostGroup.delete_template(db, data["host_group_uuid"], data["template_uuid"])
        except Exception as e:
            message = f"host_group unbound template error"
            logger.exception(f"{message} data:{data} => {e}")
            return resp_500(message)

        return result



    def task_unbound_template_start(self, db: Session, data: dict):
        """
        主机组绑定模板的异步任务启动函数
        """
        # 启动异步任务
        from app.monitor_metrics.task import prom_host_group_unbound_template_async
        # 判断任务是否已经启动，控制只有一个任务在跑。 如果任务可以多个同时进行，则无需这一步判断。
        task_name = prom_host_group_unbound_template_async.name.split(".")[-1]
        if CeleryTask.is_progress_on_name(db, task_name):
            logger.error(f"task: {task_name} was stared")
            return False

        task = prom_host_group_unbound_template_async.delay(data)
        task_data = {
            "name": task_name,
            "tid": task.id,
            "state": task.state
        }

        logger.info(f"add task: {task_name} success")
        return task_data


    def task_unbound_template_handle(self, db: Session, data: dict):
        """
        主机组绑定模板的处理函数
        """
        # 主机组解绑模板
        host_group_info = PromHostGroup.get_object_info_by_uuid(db, data["host_group_uuid"])
        for host_info in host_group_info["hosts"]:
            result = self.prom_alarm_template_handler.host_unbound_template(db, host_info["uuid"], [data["template_uuid"]])
            if not result:
                logger.error(f"host_uuid:{host_info['uuid']} unbound template_uuid:{data['template_uuid']} failed")
                return False

        # 更新数据库中主机组的信息
        PromHostGroup.delete_template(db, data["host_group_uuid"], data["template_uuid"])
        logger.info(f"host_group_uuid:{data['host_group_uuid']} unbound template_uuid:{data['template_uuid']} success")
        return True
